'''
Created on Nov 12, 2009

@author: Owner
'''
import ai.abstract_player
import interface.ConsoleAgent as itfc
import gamemodel.state

class AIHuman(ai.abstract_player.AIPlayer):
	'''
	human player by input
	'''
	
	def __init__(self, player):
		'''
		Constructor
		'''
		ai.abstract_player.AIPlayer.__init__(self, player)
		self.lastError = None
		self.lastChoosePiece = None
		
	def get_move(self, state):
		# get all moves
		moves = state.successorMove()
		self.__moves = moves
		
		#Print last error to promote user input
		error_to_print = self.get_last_error()
		if error_to_print != None:
			itfc.ConsoleDescriptor.print_error(error_to_print)
		
		piece = self.retrievePieceToMove()
		#No available move or invalid input
		if piece == None or piece == -1:
			return piece
		#else piece coordinates secured
		
		#Then decide where to move
		coords = self.retrieveDestinationToMove()
		
		#Error occurred
		if coords == -1:
			return coords
		
		self.clear_last_error()
		self.clear_last_piece()
		
		micro_move = gamemodel.state.MicroMove(\
			piece[0], piece[1], coords[0], coords[1])
		
		checker_move = gamemodel.state.CheckerMove\
			.constructByMicroMoves([micro_move])
		
		return checker_move
	
	def get_last_error(self):
		return self.lastError
	
	def clear_last_error(self):
		self.lastError = None
		
	def clear_last_piece(self):
		self.lastChoosePiece = None
	
	def retrievePieceToMove(self):
		
		#There are undecided moves
		if self.lastChoosePiece != None:
			return self.lastChoosePiece
			
		#Output possible moves
		moves = self.__moves
		movable_pieces = AIHuman.getMovablePieces(moves)
		self.__moveable_pieces = movable_pieces
		
		itfc.ConsoleDescriptor.printPossibleMoves(movable_pieces)
		
		# No move to take
		if len(movable_pieces) == 0:
			self.clear_last_error()
			return None
					
		#Choose piece to move
		
		r = itfc.ConsoleDescriptor.retrieveInputPiece()
		
		if r[0] == None or r[0] not in movable_pieces:
			self.lastError = "Invalid Input[{0}] for selecting a piece"\
				.format(r[1])
			return -1
		
		self.lastChoosePiece = r[0]
		
		return self.lastChoosePiece
	
	def retrieveDestinationToMove(self):
		moves = self.__moves
		movable_pieces = self.__moveable_pieces
		
		#Query where to move
		available_destinations = \
			AIHuman.getAvailableDestinations(moves, self.lastChoosePiece)
			
		itfc.ConsoleDescriptor.printAvailableDestinations(self.lastChoosePiece, available_destinations)
		
		#if there are multiple movable pieces, the user have rights to re-choose
		bChangePiece = len(movable_pieces) != 1
		r = itfc.ConsoleDescriptor.retrieveInputDestination(bChangePiece)
		
		#if user sent command re-choose
		if r == 'c':
			if bChangePiece: # ok to change piece
				self.clear_last_piece() #make the piece re-choosable
				self.clear_last_error()
				return -1
			else: # not ok, then next frame do the same thing
				self.lastError = "No piece other than {0} could be chosen"\
					.format(itfc.ConsoleDescriptor.coordToPresentation(\
						self.lastChoosePiece))
				return -1
		
		if r[0] == None or r[0] not in available_destinations:
				self.lastError = "Invalid Input[{0}] for selecting a destinatio to move"\
					.format(r[1])
				return -1
			
		#safely executed and got a coordinates pair	
		return r[0]
		
	@staticmethod
	def getMovablePieces(moves):
		_set = set()
		for move in moves:
			macro_move = move.list[0]
			_set.add(macro_move.original)
		_set = list(_set)
		_set.sort()
		
		return _set
	
	@staticmethod
	def getAvailableDestinations(moves, piece):
		_set = set()
		for move in moves:
			macro_move = move.list[0]
			if macro_move.original == piece:
				_set.add(macro_move.destination)
		_set = list(_set)
		_set.sort()
		
		return _set
	